package thesis.ramstorage.application;

import java.util.Observable;
import java.util.Scanner;

import thesis.ramstorage.core.PhysicalNode;
import thesis.ramstorage.math.Evaluator;
import thesis.ramstorage.math.StatisticParameters;
import thesis.ramstorage.util.EvaluatedParameters;
import thesis.ramstorage.util.Util;

public class Simulator extends Observable implements Runnable {

    private static long identityIndex = 500001;
    private static int hashKeySize;

    /**
     * @param args
     */
    public static void main(String[] args) {
        //load schema
        SystemConfiguration config = Util.loadConfig();
        //evaluate shema
        identityIndex = config.getSimulatedData();
        NetworkControl netCtrl = new NetworkControl(config);
        netCtrl.initialize();
//		print out statistical

        netCtrl.simulated();
//		print out statistical
        hashKeySize = config.getHashKeySize();

        statistic(netCtrl, config.getHashKeySize(), false);


        RealSimulator rs = new RealSimulator(netCtrl);
        Thread autoThread = new Thread(rs);
        autoThread.start();

        //start to simulate follow user input
        String input;
        Scanner in = new Scanner(System.in);
        // Reads a single line from the console
        // and stores into name variable
        input = in.nextLine();
        // Reads a integer from the console
        // and stores into age variable

        // Prints name and age to the console

        //ghi log thay vi sysout

        while (!Operation.END.toString().equalsIgnoreCase(input)) {
            try {
                if (!netCtrl.isManualSimulation()) {
                    autoSimulate(netCtrl, config);
                    break;//comment if dynamic chart
                }

                if (input == null || input.equals("")) {
                    continue;
                }
                String[] operations = input.trim().split(" ");
                String op = operations[0];
                if (operations.length > 1) {
                    String param = operations[1];
                    execute(netCtrl, op, param);
                } else {
                    execute(netCtrl, op, null);
                }

            } catch (Exception e) {
                e.printStackTrace();
                System.out.println(" command error! Please try again. ");
            }
            input = in.nextLine();
        }
        System.out.println(" Simulator is terminated.");
        //listen arguments and continue simulated
        //print out statistical

    }

    private static void autoSimulate(NetworkControl netCtrl, SystemConfiguration config) {
        // TODO Auto-generated method stub
        // TODO Auto-generated method stub
        try {
            String[] operations = config.getScenario().split("->");
            for (String opr : operations) {
                String[] messages = opr.trim().split(" ");
//					messages[0];
                execute(netCtrl, messages[0], messages[1]);
                StatisticParameters sp = statistic(netCtrl, hashKeySize, false);
            }
        } catch (Exception e) {
            System.out.println("error when setting scenario");
        }
    }

    private static void execute(NetworkControl netCtrl, String op, String param) {
        // TODO Auto-generated method stub
        if (Operation.HELP.toString().equalsIgnoreCase(op)) {
            System.out.println(" ==========Distributed Ram Storage Help==========");
            System.out.println(" .Network print out: NET_PRINT_OUT");
            System.out.println(" .Data pushing : DATA_ADD [number of data object]");
            System.out.println(" .NODE joining : NODE_JOIN [ID of Node]");
            System.out.println(" .NODE leave: NODE_SHUTDOWN [ID of Node]");
            System.out.println(" .NODE load: NODE_LOAD [ID of Node]");
            System.out.println(" .NODE capacity : NODE_CP [ID of Node]");
            System.out.println(" .NODE print : NODE_PRINT [ID of Node]");
            System.out.println(" .Evaluate: EV");
            System.out.println(" .Flush data: FLUSH");
            System.out.println(" .Auto simulate: AUTO");
            System.out.println(" .Reboot network: REBOOT");
            System.out.println(" .End Simulator: END");
        }
        if (Operation.NET_PRINT.toString().equalsIgnoreCase(op)) {
            netCtrl.printOut();
        } else if (Operation.SHUTDOWN.toString().equalsIgnoreCase(op)) {
            int nodeId = Integer.parseInt(param.trim());
            System.out.println(" Node Id: " + nodeId + " is shutting down...");
            //node shutdown
            netCtrl.shutdown(nodeId);
            System.out.println(" Node Id: " + nodeId + " is shutdown.");
        } else if (Operation.RANDOM_JOIN.toString().equalsIgnoreCase(op)) {
            int nodeId = Integer.parseInt(param.trim());//in.nextInt();
            System.out.println(" Node Id: " + nodeId + " is joining...");
//        	newNode.setNumberOfVirtualNode(netCtrl.randomNodeCapacity());
            try {
//				netCtrl.join(newNode);
                netCtrl.randomlyJoin(nodeId);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(" Node Id: " + nodeId + " has joined.");
//			statistic(netCtrl, hashKeySize, false);
        } else if (Operation.JOIN.toString().equalsIgnoreCase(op)) {
            int nodeId = Integer.parseInt(param.trim());//in.nextInt();
            System.out.println(" Node Id: " + nodeId + " is joining...");
//        	newNode.setNumberOfVirtualNode(netCtrl.randomNodeCapacity());
            try {
//				netCtrl.join(newNode);
                netCtrl.join(nodeId);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(" Node Id: " + nodeId + " has joined.");
//			statistic(netCtrl, hashKeySize, false);
        } else if (Operation.FIND_AND_JOIN.toString().equalsIgnoreCase(op)) {
            int nodeId = Integer.parseInt(param.trim());//in.nextInt();
            System.out.println(" Node Id: " + nodeId + " is joining after looking up location...");
            try {
                netCtrl.lookupAndJoin(nodeId);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(" Node Id: " + nodeId + " has joined.");
//			statistic(netCtrl, hashKeySize, false);
        } else if (Operation.NODE_LOAD.toString().equalsIgnoreCase(op)) {
            int nodeId = Integer.parseInt(param.trim());//in.nextInt();
            try {
                System.out.println(" Node Id: " + nodeId + " has load " + netCtrl.getNodeLoad(nodeId));
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        } else if (Operation.NODE_VN.toString().equalsIgnoreCase(op)) {
            int nodeId = Integer.parseInt(param.trim());//in.nextInt();
            try {
                System.out.println(" Node Id: " + nodeId + " has number of virtual node:  " + netCtrl.nodes.get(nodeId).getNumberOfVirtualNode());
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        } else if (Operation.NODE_PRINT.toString().equalsIgnoreCase(op)) {
            int nodeId = Integer.parseInt(param.trim());//in.nextInt();
            try {
                netCtrl.nodePrint(nodeId);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        } else if (Operation.EV.toString().equalsIgnoreCase(op)) {
            if (!Util.isEmptyString(param) && param.equals("-c")) {
                statistic(netCtrl, hashKeySize, true);
            } else {
                statistic(netCtrl, hashKeySize, false);
            }
        } else if (Operation.MIN_LOAD.toString().equalsIgnoreCase(op)) {
            System.out.println(netCtrl.getMinLoad());
        } else if (Operation.DATA_ADD.toString().equalsIgnoreCase(op)) {
            int numberOfDataObj = Integer.parseInt(param.trim());//in.nextInt();
            System.out.println(numberOfDataObj + " data objects is adding now...");
            autoInputData(netCtrl, numberOfDataObj);
//    		statistic(netCtrl, hashKeySize, false);
            System.out.println(numberOfDataObj + " data objects has been added.");
        } else if (Operation.FLUSH.toString().equals(op)) {
            netCtrl.flushData();
        }
    }

    private static void autoInputData(NetworkControl net, int numberOfDataObj) {
        // TODO Auto-generated method stub
        for (int i = 0; i < numberOfDataObj; i++) {
            long newKey = autoGenerateKey();
            DataObj dataObj = new DataObj();
            dataObj.setId(newKey);
            try {
                net.put(dataObj);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                System.out.println(e.getMessage());
                break;
            }
        }
    }

    private static long autoGenerateKey() {
        // TODO Auto-generated method stub
        return (identityIndex++);
    }

    private static StatisticParameters statistic(NetworkControl netCtrl, int ks, boolean showChart) {
        EvaluatedParameters evaParams = netCtrl.evaluate();
        Evaluator evaluator = null;
        if (showChart) {
            evaluator = new Evaluator(evaParams, true);
        } else {
            evaluator = new Evaluator(evaParams, false);
        }
//		evaluator.evaluate(netCtrl);
//		long totalLoad = 0;
//		for(int i = 0; i < netCtrl.getSize() ; i++){
//			long nl = netCtrl.getNodeLoad(Long.valueOf(i));
//            double value = nl/netCtrl.getNodeCapacity(i);
//            totalLoad += nl;
//            evaluator.addValue(value);
//	    }
        StatisticParameters res = new StatisticParameters();
        System.out.println("capacity : " + Math.pow(2, ks));
        System.out.println("total load : " + evaluator.getNetworkLoad());
        System.out.println("% load : " + evaluator.getNetworkLoad() / Math.pow(2, ks) * 100);
        System.out.println(" mean " + evaluator.getMean());
        double _std = evaluator.getStandardDeviation() / evaluator.getMean() * 100;
        res.setStandardDeviation(_std);
        System.out.println(" std % " + _std);
        System.out.println(" node max load " + evaluator.getNodeMaxLoad().getID() + " with load: " + evaluator.getNodeMaxLoad().getLoad());
        System.out.println(" node min load " + evaluator.getNodeMinLoad().getID() + " with load: " + evaluator.getNodeMinLoad().getLoad());
        return res;

    }

    public void run() {
        // TODO Auto-generated method stub
        SystemConfiguration config = Util.loadConfig();
        //evaluate shema
        identityIndex = config.getSimulatedData();
        NetworkControl netCtrl = new NetworkControl(config);
        netCtrl.initialize();
//		print out statistical

        netCtrl.simulated();
//		print out statistical
        hashKeySize = config.getHashKeySize();
        try {
            String[] operations = config.getScenario().split("->");
            for (String opr : operations) {
                String[] messages = opr.trim().split(" ");
//					messages[0];
                execute(netCtrl, messages[0], messages[1]);
                StatisticParameters sp = statistic(netCtrl, hashKeySize, false);
                setChanged();
                notifyObservers(sp.getStandardDeviation());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
//		for(int i = 0; i < 10; i++){
//			setChanged();
//			notifyObservers(i);
//			try {
//				Thread.sleep(1000);
//			} catch (InterruptedException e) {
//				// TODO Auto-generated catch block
//				e.printStackTrace();
//			}
//			i++;
//		}
    }
}
